﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FrameWork.Source.Object_Classes;
using Microsoft.Xna.Framework;
using FrameWork.Source.Global_variables;

namespace Boomer.Source.QuadTree
{
    class CQuadTree
    {

        #region Variables

        long width;
        long height;
        ulong maxLevel;
        CQuadTreeNode pRootNode;

        #endregion


        #region Constructor

        public CQuadTree(ulong maxLevel, long width, long height)
        {
            this.maxLevel = maxLevel;
            this.width = width;
            this.height = height;

            pRootNode = new CQuadTreeNode(null, 0, 0, width, height);
        }

        #endregion


        #region Methods

        public bool subDivide(CQuadTreeNode pNode)
        {
            if (pNode.nodeLevel < this.maxLevel)
            {
                if (pNode.pSubNode == null)
                {
                    long nodeLeftX = pNode.upperLeftX;
                    long nodeLeftY = pNode.upperLeftY;
                    long nodeWidth = pNode.width / 2;
                    long nodeHeight = pNode.height / 2;

                    pNode.pSubNode = new CQuadTreeNode[4];

                    pNode.pSubNode[0] = new CQuadTreeNode(pNode, nodeLeftX, nodeLeftY, nodeWidth, nodeHeight);
                    pNode.pSubNode[1] = new CQuadTreeNode(pNode, nodeLeftX + nodeWidth, nodeLeftY, nodeWidth, nodeHeight);
                    pNode.pSubNode[2] = new CQuadTreeNode(pNode, nodeLeftX, nodeLeftY + nodeHeight, nodeWidth, nodeHeight);
                    pNode.pSubNode[3] = new CQuadTreeNode(pNode, nodeLeftX + nodeWidth, nodeLeftY + nodeHeight, nodeWidth, nodeHeight);

                    return true;
                }
                else
                    return true;
            }

            return false;
        }


        public void addObject(CObject pOjbect)
        {
            addObject(pOjbect, null);
        }


        private void addObject(CObject pObject, CQuadTreeNode pNode)
        {
            if (pNode == null)
                pNode = this.pRootNode;

            ulong i = 0;
            ulong collisionIndex = 0;
            int[] index = { -1 , -1 };
            Rectangle[] virtualNodeRect = new Rectangle[4];
            long nodeLeftX = pNode.upperLeftX;
            long nodeLeftY = pNode.upperLeftY;
            long nodeWidth = pNode.width / 2;
            long nodeHeight = pNode.height / 2;

            virtualNodeRect[0] = new Rectangle((int)nodeLeftX, (int)nodeLeftY, (int)nodeWidth, (int)nodeHeight);
            virtualNodeRect[1] = new Rectangle((int)(nodeLeftX + nodeWidth), (int)nodeLeftY, (int)nodeWidth, (int)nodeHeight);
            virtualNodeRect[2] = new Rectangle((int)nodeLeftX, (int)(nodeLeftY + nodeHeight), (int)nodeWidth, (int)nodeHeight);
            virtualNodeRect[3] = new Rectangle((int)(nodeLeftX + nodeWidth), (int)(nodeLeftY + nodeHeight), (int)nodeWidth, (int)nodeHeight);

            for (i = 0; i < 4; ++i)
            {
                if (pObject.getBoundBox().Intersects(virtualNodeRect[i]))
                {
                    index[collisionIndex++] = (int)i;
                    if (collisionIndex > 1)
                        break;
                }
            }

            if (collisionIndex == 0)
            {
                return;
            }
            if (collisionIndex > 1)
            {
                pNode.addObject(pObject);
            }
            else
            {
                if (subDivide(pNode))
                {
                    addObject(pObject, pNode.pSubNode[index[0]]);
                }
                else
                {
                    pNode.addObject(pObject);
                }
            }
        }

        public void getObject(Rectangle recBound, ref List<CObject> pList)
        {
            getObject(recBound, ref pList, null);
        }

        private void getObject(Rectangle recBound, ref List<CObject> pList, CQuadTreeNode pNode)
        {
            if (pNode == null)
                pNode = this.pRootNode;

            if (pNode.pSubNode == null)
            {
                pNode.getObject(ref pList);
            }
            else
            {
                pNode.getObject(ref pList);
                int[] index = { -1, -1, -1, -1 };
                ulong i, collisionIndex = 0;

                for (i = 0; i < 4; ++i)
                {
                    if (recBound.Intersects(pNode.pSubNode[i].getSize()))
                    {
                        index[collisionIndex++] = (int)i;
                    }
                }

                for (i = 0; i < collisionIndex; ++i)
                {
                    getObject(recBound, ref pList, pNode.pSubNode[index[i]]);
                }
            }
        }

        public void removeObject(CObject pObject)
        {
            removeObject(pObject, null);
        }

        private void removeObject(CObject pObject, CQuadTreeNode pNode)
        {
            if (pNode == null)
                pNode = this.pRootNode;

            if (pNode.removeObject(pObject))
                return;

            if (pNode.pSubNode == null)
            {
                return;
            }
            else
            {
                int[] index = { -1, -1, -1, -1 };
                ulong i, collisionIndex = 0;

                for (i = 0; i < 4; ++i)
                {
                    if (pObject.getBoundBox().Intersects(pNode.pSubNode[i].getSize()))
                    {
                        index[collisionIndex++] = (int)i;
                    }
                }

                for (i = 0; i < collisionIndex; ++i)
                {
                    removeObject(pObject, pNode.pSubNode[index[i]]);
                }
            }
        }

        #endregion


        #region Draw and Render

        public void update(float deltaTime)
        {
            List<CObject> listUpdate = new List<CObject>();
            List<CObject> listCollision = new List<CObject>();
            Rectangle cameraSize = new Rectangle(0, 0, CGLOBAL.g_iScreenWidth, CGLOBAL.g_iScreenHeight);

            int sizeUpdate = 0, sizeCollision = 0;
            int i, j;

            getObject( cameraSize, ref listUpdate);
            sizeUpdate = listUpdate.Count;

            for (i = 0; i < sizeUpdate; ++i)
            {
                removeObject(listUpdate[i]);

                if( listUpdate[i].m_isAlive )
                    listUpdate[i].update(deltaTime);

                listCollision.Clear();
                getObject(cameraSize, ref listCollision);
                sizeCollision = listCollision.Count;

                for (j = 0; j < sizeCollision; ++j)
                {
                    CObject pObject = listCollision[j];
                    listUpdate[i].Collision(ref pObject);
                }

                CObject pMainObj = listUpdate[i];
                MapItem.MapItemManager.getInstance().collision(ref pMainObj);

                if( listUpdate[i].m_isAlive)
                    addObject(listUpdate[i]);
            }
        }

        public void draw()
        {
            List<CObject> listRender = new List<CObject>();

            int size = 0;
            Rectangle cameraSize = new Rectangle(0, 0, CGLOBAL.g_iScreenWidth, CGLOBAL.g_iScreenHeight);

            getObject(cameraSize, ref listRender);
            size = listRender.Count;

            for (int i = 0; i < size; ++i)
            {
                listRender[i].draw();
            }

            MapItem.MapItemManager.getInstance().draw();

        }

        #endregion
    }
}
